package birdsong.client;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;



import birdsong.comm.ClientComm;
import birdsong.comm.ClientSideMessage;

/**
 * Birdsong clients communicate with a Birdsong server exclusively through this interface. 
 * 
 * @author Joao 
 * @version 1
 */
public class BirdClientComm implements ClientComm {
	
	// Server Port - not implemented to change at run time.
	private static final int SERVER_PORT = 8080;
	
	private Socket socket;
	private ObjectInputStream in;
	private ObjectOutputStream out;
	
	@Override
	/**
	 * Establish a connection to a Birdsong server. This method should be the first one called by 
	 * the client as no other operation (expect for {@link #isConnected()}) can be performed before
	 * a connection to the server has been established.
	 * 
	 * @param host                    Address of the server. For instance "localhost", "192.168.1.1", and so on.
	 * @param nickname                The nickname to use for the client.
	 * @throws UnknownHostException   Thrown if the host cannot be found.
	 * @throws IOException            Thrown if a connection cannot be established.
	 */
	public void connect(String serverAddress, String nickname) throws IOException {
		InetAddress serverAdd;
		serverAdd = InetAddress.getByName(serverAddress);
		socket = new Socket(serverAdd, SERVER_PORT);
		in = new ObjectInputStream(socket.getInputStream());
		out = new ObjectOutputStream(socket.getOutputStream());
		out.writeObject(nickname);
	}

	@Override
	/**
	 * Check if currently connected to a host.
	 * 
	 * @return <b>true</b> if currently connected to a host, <b>false</b> otherwise.
	 */
	public boolean isConnected() {
		if (socket!=null) {
			if (socket.isConnected()) {
				return true;
			}
		}
		return false; 
	}

	@Override
	/**
	 * Disconnect from a Birdsong server. Any sockets should be closed immediately. 
	 * Pending messages are not guaranteed to be delivered before disconnecting.
	 */	
	public void disconnect() {
		if (socket!=null) {
		try {
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		}
	}

	@Override
	/** 
	 * Check if a message from the server is pending. If {@link #hasNextMessage()} returns <b>true</b>, a call to {@link #getNextMessage()} 
	 * will return immediately with the oldest message in the queue. If {@link #hasNextMessage()} returns <b>false</b>, a call to {@link #getNextMessage()} 
	 * will block until a message has been received.
	 * 
	 * @return <b>true</b> if a message from the server is currently pending, otherwise <b>false</b>.
	 */
	public boolean hasNextMessage() {
		try {
			if (in!=null){
				if (in.available()>0){
					return true;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;	
	}

	@Override
	/**
	 * Get the next message received from the server. If no message 
	 * is has been received, the method blocks until a message has 
	 * been received. If no message will ever be received (connection is down), null is returned.
	 * 
	 * @return The next message sent by the server to the client or null if no message will ever be received (disconnected from server).
	 */
	public ClientSideMessage getNextMessage() {
		ClientSideMessage inMessage = null; 
		try {
			inMessage = (ClientSideMessage) in.readObject();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			inMessage = null; 
		} catch (IOException e) {
			e.printStackTrace();
			inMessage = null; 
		}
		return inMessage;
	}

	@Override
	/**
	 * Send a Birdsong message from this client to the server which should then relay the message to all followers.
	 * 
	 * @param birdsongMessage The Birdsong message to be sent.
	 */
	public void sendBirdsongMessage(String birdsongMessage) {
		if (in!=null){
			try {
				out.writeObject(birdsongMessage);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	/**
	 * Request to follow another client.
	 * 
	 * @param nickname the nickname of the client that should be followed.
	 */
	public void follow(String nickname) {
		try {
			out.writeObject(nickname);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	/**
	 * Request to unfollow a client.
	 * 
	 * @param nickname the nickname of the client currently followed that should be unfollowed.
	 */
	public void unfollow(String nickname) {
		try {
			out.writeObject(nickname);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}
